#include "audio_event_iface.h"
#include "audio_pipeline.h"
#include "bluetooth_service.h"
#include "board.h"
#include "esp_bt.h"
#include "esp_bt_defs.h"
#include "esp_bt_device.h"
#include "esp_bt_main.h"
#include "esp_gap_bt_api.h"
#include "esp_hf_client_api.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "filter_resample.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "i2s_stream.h"
#include "opus_decoder.h"
#include "raw_stream.h"
#include "user_bt.h"
#include "user_codec.h"
#include "user_utils.h"
#include <string.h>

#define TAG "user player"

static audio_element_handle_t userDataIn_stream_init(void);
static void opus_err_process(void);
static void relink_pipeline(audio_source_config_t config);

static const char *ble_link_tag[4] = {"user_reader", "decoder", "resample", "i2s_writer"};
static const char *bt_link_tag[2] = {"bt_reader", "i2s_writer"};

static audio_pipeline_handle_t player_pipeline;
audio_element_handle_t i2s_writer; // 对外提供的elements,用于统一的配置i2s参数
static audio_element_handle_t user_data_in, opus_decoder, resample;
static audio_event_iface_handle_t evt;
static audio_element_info_t music_info;
static bool need_relink_pipeline = false;
static audio_source_config_t source_config;

void player_task(void *param) {
    // 1. 构建各类elements
    bt_stream_reader = bluetooth_service_create_stream(); // 在bt init内已创建
    user_data_in = userDataIn_stream_init();
    ESP_LOGI(TAG, "input elements create: user_data_in, bt_stream_reader");

    opus_decoder_cfg_t opus_dec_cfg = DEFAULT_OPUS_DECODER_CONFIG();
    opus_decoder = decoder_opus_init(&opus_dec_cfg);
    rsp_filter_cfg_t rsp_cfg = DEFAULT_RESAMPLE_FILTER_CONFIG();
    rsp_cfg.src_rate = 48000;  // 由于 Opus 可能默认解码成 48kHz
    rsp_cfg.src_ch = 1;        // 单声道
    rsp_cfg.dest_rate = 16000; // 目标是 16kHz
    rsp_cfg.dest_ch = 1;       // 目标单声道
    resample = rsp_filter_init(&rsp_cfg);
    ESP_LOGI(TAG, "process elements create: opus_decoder, resample");

    i2s_stream_cfg_t i2s_cfg_w = I2S_STREAM_CFG_DEFAULT_WITH_TYLE_AND_CH(CODEC_ADC_I2S_PORT, 16000, 16, AUDIO_STREAM_WRITER, 1);
    i2s_cfg_w.type = AUDIO_STREAM_WRITER;
    i2s_writer = i2s_stream_init(&i2s_cfg_w);
    ESP_LOGI(TAG, "output elements create: i2s_writer");

    // 创建pipeline并组装
    audio_pipeline_cfg_t player_pipeline_cfg = DEFAULT_AUDIO_PIPELINE_CONFIG();
    player_pipeline = audio_pipeline_init(&player_pipeline_cfg);
    audio_pipeline_register(player_pipeline, user_data_in, "user_reader");
    audio_pipeline_register(player_pipeline, bt_stream_reader, "bt_reader");
    audio_pipeline_register(player_pipeline, opus_decoder, "decoder");
    audio_pipeline_register(player_pipeline, resample, "resample");
    audio_pipeline_register(player_pipeline, i2s_writer, "i2s_writer");
    audio_pipeline_link(player_pipeline, bt_link_tag, 2);

    // 创建监听
    evt = audio_event_iface_init(&(audio_event_iface_cfg_t)AUDIO_EVENT_IFACE_DEFAULT_CFG());
    audio_pipeline_set_listener(player_pipeline, evt);

    // 运行
    audio_pipeline_run(player_pipeline);

    while (1) {
        if (need_relink_pipeline == true) {
            need_relink_pipeline = false;
            relink_pipeline(source_config);
        }

        audio_event_iface_msg_t msg;
        esp_err_t ret = audio_event_iface_listen(evt, &msg, 100 / portTICK_PERIOD_MS);
        if (ret != ESP_OK) {
            continue;
        }
        const char *source_tag = audio_element_get_tag((audio_element_handle_t)msg.source);
        if (msg.cmd == AEL_MSG_CMD_REPORT_STATUS) {
            audio_element_state_t state = audio_element_get_state((audio_element_handle_t)msg.source);
            ESP_LOGW(TAG, "[Event] %s reported status: %d", source_tag, state);
        } else {
            ESP_LOGI(TAG, "[Event] %s cmd: 0x%x", source_tag, msg.cmd);
        }
        // opus 异常,一般由帧丢失引起
        if (msg.source_type == AUDIO_ELEMENT_TYPE_ELEMENT && msg.source == (void *)opus_decoder && msg.cmd == AEL_MSG_CMD_REPORT_STATUS &&
            (int)msg.data == AEL_STATUS_ERROR_PROCESS) {
            ESP_LOGW(TAG, "[ * ] opus decoder error process");
            opus_err_process();
        }
        // 蓝牙
        if (msg.source_type == AUDIO_ELEMENT_TYPE_ELEMENT && msg.source == (void *)bt_stream_reader &&
            msg.cmd == AEL_MSG_CMD_REPORT_MUSIC_INFO) {
            audio_element_getinfo(bt_stream_reader, &music_info);

            ESP_LOGI(TAG, "[ * ] Receive music info from Bluetooth, sample_rates=%d, bits=%d, ch=%d", music_info.sample_rates,
                     music_info.bits, music_info.channels);
            // user_set_i2s_clk(i2s_reader, i2s_writer, music_info.sample_rates, music_info.bits, music_info.channels);
            i2s_stream_set_clk(i2s_writer, music_info.sample_rates, music_info.bits, music_info.channels);
            continue;
        }
        if (msg.source_type == PERIPH_ID_BLUETOOTH && msg.source == (void *)bt_periph) {
            if (msg.cmd == PERIPH_BLUETOOTH_DISCONNECTED) {
                ESP_LOGW(TAG, "[ * ] Bluetooth disconnected");
            }
        }
        if (msg.source_type == AUDIO_ELEMENT_TYPE_ELEMENT && msg.source == (void *)i2s_writer && msg.cmd == AEL_MSG_CMD_REPORT_STATUS &&
            (int)msg.data == AEL_STATUS_STATE_STOPPED) {
            ESP_LOGW(TAG, "[ * ] Stop event received");
        }
    }
    // 不会到这里
}

typedef struct {
    bool enable[AUDIO_SOURCE_NUM];
    audio_source_type_t target;
} player_link_state_t;
player_link_state_t player_link_state = {
    .target = AUDIO_SOURCE_BLE,
    .enable = {true, false},
};

static const char *link_tag[] = {"BT", "BLE"};
void player_reconfigure(audio_source_config_t config) {

    player_link_state.enable[config.source] = config.enable;
    for (int i = AUDIO_SOURCE_NUM - 1; i >= 0; i--) {
        if (player_link_state.enable[i]) {
            source_config.enable = true;
            source_config.source = i;
            ESP_LOGI(TAG, "[ * ] Source %s enabled", link_tag[i]);
            // relink_pipeline(source_config);
            need_relink_pipeline = true;
            return;
        }
    }
    source_config.enable = false;
    source_config.source = AUDIO_SOURCE_BLE;
    ESP_LOGI(TAG, "[ * ] No source enabled, stopping pipeline");
    // relink_pipeline(source_config);
    need_relink_pipeline = true;
}

static void relink_pipeline(audio_source_config_t config) {
    static audio_source_type_t last_source = AUDIO_SOURCE_BT;
    ESP_LOGI(TAG, "[ * ] Source changed to %s, %s", link_tag[config.source], config.enable ? "enabled" : "disabled");

    audio_pipeline_stop(player_pipeline);
    audio_pipeline_wait_for_stop_with_ticks(player_pipeline, 1000 / portTICK_PERIOD_MS);
    audio_pipeline_terminate(player_pipeline);
    ESP_LOGI(TAG, "pipeline stopped, reconfigure pipeline");

    if (last_source != config.source) {
        last_source = config.source;
        if (config.source == AUDIO_SOURCE_BLE) {
            audio_pipeline_breakup_elements(player_pipeline, bt_stream_reader);
            audio_pipeline_register(player_pipeline, i2s_writer, "i2s_writer");
            i2s_stream_set_clk(i2s_writer, 16000, 16, 1);
            audio_pipeline_relink(player_pipeline, ble_link_tag, 4);
            ESP_LOGI(TAG, "Linking BLE stream to I2S writer");
        } else {
            audio_pipeline_breakup_elements(player_pipeline, user_data_in);
            audio_pipeline_breakup_elements(player_pipeline, opus_decoder);
            audio_pipeline_breakup_elements(player_pipeline, resample);
            i2s_stream_set_clk(i2s_writer, music_info.sample_rates, music_info.bits, music_info.channels);
            audio_pipeline_relink(player_pipeline, bt_link_tag, 2);
            ESP_LOGI(TAG, "Linking BT stream to I2S writer");
        }
    }

    if (config.enable) {
        audio_pipeline_reset_ringbuffer(player_pipeline);
        audio_pipeline_reset_elements(player_pipeline);
        audio_pipeline_change_state(player_pipeline, AEL_STATE_INIT);
        audio_pipeline_remove_listener(player_pipeline);
        audio_pipeline_set_listener(player_pipeline, evt);

        audio_pipeline_run(player_pipeline);
        ESP_LOGI(TAG, "Pipeline running");
    } else {
        ESP_LOGI(TAG, "Pipeline stop");
    }
}

static void opus_err_process() {
    audio_pipeline_stop(player_pipeline);
    audio_pipeline_wait_for_stop_with_ticks(player_pipeline, 1000 / portTICK_PERIOD_MS);
    audio_pipeline_terminate(player_pipeline);

    audio_pipeline_reset_ringbuffer(player_pipeline);
    audio_pipeline_reset_elements(player_pipeline);
    audio_pipeline_change_state(player_pipeline, AEL_STATE_INIT);
    audio_pipeline_remove_listener(player_pipeline);
    audio_pipeline_set_listener(player_pipeline, evt);

    audio_pipeline_run(player_pipeline);
}
#define USER_DATA_IN_PROCESS
#ifdef USER_DATA_IN_PROCESS
typedef struct {
    bool is_playing;
    decoder_data_in data_in;
} userDataIn_state_t;
userDataIn_state_t userDataIn_state;

// 注册 userDataIn data in函数
void userDataIn_regist_handler(decoder_data_in handler) {
    userDataIn_state.data_in = handler;
    return;
}

// userDataIn open
static esp_err_t userDataIn_open(audio_element_handle_t self) {
    ESP_LOGI(TAG, "userDataIn element opened");
    return ESP_OK;
}

// userDataIn close
static esp_err_t userDataIn_close(audio_element_handle_t self) {
    ESP_LOGI(TAG, "userDataIn element closed");
    return ESP_OK;
}

static int userDataIn_read(audio_element_handle_t self, char *buffer, int len, TickType_t ticks_to_wait, void *context) {
    int ret = userDataIn_state.data_in((uint8_t *)buffer, len);
    if (ret == 0) {
        ESP_LOGW(TAG, "No more data, ret:%d", ret);
    } else {
        ESP_LOGI(TAG, "Read %d bytes", ret);
        ESP_LOG_BUFFER_HEX(TAG, buffer, 10);
        audio_element_update_byte_pos(self, ret);
    }
    return ret;
}

// userDataIn process
static int userDataIn_process(audio_element_handle_t self, char *in_buffer, int in_len) {
    int r_size = audio_element_input(self, in_buffer, in_len);
    int w_size = 0;
    if (r_size > 0) {
        w_size = audio_element_output(self, in_buffer, r_size);
    } else {
        w_size = r_size;
    }
    return w_size;
}

// userDataIn destory
static esp_err_t userDataIn_destory(audio_element_handle_t self) {
    ESP_LOGI(TAG, "Destroying userDataIn element");
    return ESP_OK;
}

// userDataIn element init
static audio_element_handle_t userDataIn_stream_init(void) {
    audio_element_cfg_t cfg = DEFAULT_AUDIO_ELEMENT_CONFIG();
    cfg.open = userDataIn_open;
    cfg.close = userDataIn_close;
    cfg.process = userDataIn_process;
    cfg.destroy = userDataIn_destory;
    cfg.task_stack = 4 * 1024;
    cfg.out_rb_size = 2 * 1024;
    cfg.buffer_len = 2048;
    cfg.read = userDataIn_read;
    cfg.tag = "userDataIn";
    cfg.stack_in_ext = false;

    return audio_element_init(&cfg);
}

#endif